package co.bugu.code.analysis.service;

import co.bugu.code.analysis.service.entity.Branch;
import co.bugu.code.analysis.service.entity.CommitInfo;
import co.bugu.code.analysis.service.entity.GitInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.*;

/**
 * 主要用于gitlab获取相关的数据
 *
 * @author daocers
 * @date 2025/3/18 15:11
 */
@Slf4j
@Service
public class GitService {

    private static final String GITLAB_API_URL = "https://gitlab.lbxdrugs.com/api/v4"; // GitLab API地址
    private static final String ACCESS_TOKEN = "glpat-T2JjcyzfwcYfnjyi8CXK"; // todo: 替换为你的GitLab Token

    /**
     * 工作目录， 所有的代码和统计结果将保存在这个目录下
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/29 18:03
     */
    private String workspaceDir = "d:/worker/";

    public void setWorkspaceDir(String workspaceDir) {
        this.workspaceDir = workspaceDir;
    }

    public String getWorkspaceDir() {
        return workspaceDir;
    }

    /**
     * 获取所有git项目
     * 获取git信息，但是不拉取数据
     *
     * @param withBranch 是否携带branch信息
     * @return
     * @author alei
     * @date 2025/3/21 9:13
     */
    public List<GitInfo> getGitProjectList(boolean... withBranch) throws IOException, JSONException {
        List<GitInfo> list = new ArrayList<>();
        int page = 1;
        boolean hasMore = true;
        while (hasMore) {
            String apiUrl = GITLAB_API_URL + "/projects?per_page=100&page=" + page; // 每页100个项目
            String res = this.getByApi(apiUrl);
            if (StringUtils.isBlank(res)) {
                log.error("获取所有git工程失败", res);
                break;
            }

            // 解析JSON响应
            JSONArray projects = new JSONArray(res);
            for (int i = 0; i < projects.length(); i++) {
                JSONObject project = projects.getJSONObject(i);
                String httpUrl = project.getString("http_url_to_repo");
                String path = project.getString("path");
                String fullPath = project.getString("path_with_namespace");
                Integer id = project.getInt("id");

                GitInfo gitInfo = new GitInfo();
                gitInfo.setProjectId(id);
                gitInfo.setPath(fullPath);
                gitInfo.setName(path);
                gitInfo.setUrl(httpUrl);


//                如果需要，处理分支信息
                boolean needBranch = false;
                if (null != withBranch && withBranch.length > 0) {
                    needBranch = withBranch[0];
                }
                if (needBranch) {
                    log.info("开始获取项目{}的分支信息", gitInfo.getPath());
                    List<Branch> branchList = this.getBranchList(id, gitInfo);
                    branchList.sort(new Comparator<Branch>() {
                        @Override
                        public int compare(Branch o1, Branch o2) {
                            return o2.getCommitTime().compareTo(o1.getCommitTime());
                        }
                    });
                    StringBuffer buffer = new StringBuffer();
                    int size = branchList.size();
                    gitInfo.setBranchCount(size);
                    if (size > 5) {
                        size = 5;
                    }
                    for (int j = 0; j < size; j++) {
                        Branch branch = branchList.get(j);
                        buffer.append(branch.getName())
                                .append(" | ")
                                .append(branch.getCommitUser())
                                .append(" | ")
                                .append(branch.getCommitTime())
                                .append(" | ")
                                .append(branch.getCommitMessage())
                                .append("\n");
                    }
                    gitInfo.setUpdateInfo(buffer.toString());
                    gitInfo.setBranchList(branchList);
                    gitInfo.setBranchCount(branchList.size());
                }

                list.add(gitInfo);
            }
            if (projects.length() == 100) {
                hasMore = true;
                page++;
            } else {
                hasMore = false;
            }
        }
        return list;
    }


    /**
     * 获取git项目的分支信息
     * 按照提交时间排序
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/21 9:14
     */
    public List<Branch> getBranchList(Integer projectId, GitInfo gitInfo) throws IOException, JSONException {
        List<Branch> branches = new ArrayList<>();
        int page = 1;

        boolean hasMorePages = true;
        while (hasMorePages) {
            String url = String.format("%s/projects/%d/repository/branches?page=%d&per_page=100", GITLAB_API_URL, projectId, page);
            String res = this.getByApi(url);
            JSONArray projects = new JSONArray(res);
            for (int i = 0; i < projects.length(); i++) {
                JSONObject obj = projects.getJSONObject(i);
                String name = obj.getString("name");

                JSONObject commit = obj.getJSONObject("commit");
                String commitId = commit.getString("id");
                String commitDate = commit.getString("committed_date");
                String committerName = commit.getString("committer_name");
                String message = commit.getString("message");

                String createUser = commit.getString("author_name");
                String createdAt = commit.getString("created_at");
                boolean isDefault = obj.getBoolean("default");


                Branch branch = new Branch();
                branch.setName(name);
                branch.setGitName(gitInfo.getPath());
                branch.setCommitId(commitId);
                branch.setCreateUser(createUser);
                branch.setCreateTime(parseStrToTime(createdAt));
                branch.setCommitTime(parseStrToTime(commitDate));
                branch.setCommitUser(committerName);
                branch.setIsDefault(isDefault);
                branch.setCommitMessage(message);
                branches.add(branch);
            }
            if (projects.length() == 100) {
                hasMorePages = true;
                page++;
            } else {
                hasMorePages = false;
            }
        }

        return branches;
    }



    /**
     * @param
     * @return
     * @author alei
     * @date 2025/3/18 21:50
     */
    public List<CommitInfo> getCommitInfo(Integer projectId, String branchName) throws IOException, JSONException, ParseException {
        int page = 1;
        String url = String.format("%s/projects/%d/repository/commits?ref_name=%s&page=%d&per_page=100", GITLAB_API_URL, projectId, branchName, page);

        List<CommitInfo> list = new ArrayList<>();
        String res = this.getByApi(url);
        JSONArray projects = new JSONArray(res);
        for (int i = 0; i < projects.length(); i++) {
            JSONObject obj = projects.getJSONObject(i);
            String title = obj.getString("title");
            String authorName = obj.getString("author_name");
            String authorEmail = obj.getString("author_email");
            String time = obj.getString("created_at");
            String message = obj.getString("message");

            CommitInfo info = new CommitInfo();
            info.setTitle(title);
            info.setAuthor(authorName);
            info.setMessage(message);
            info.setCreateTime(parseStrToTime(time));
            info.setCreatedAt(time);
            list.add(info);
        }
        return list;

    }

    /**
     * 将  2024-11-20T16:42:03.000+08:00 格式的时间转化为 date类型
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/19 9:26
     */
    private Date parseStrToTime(String utcTime) {

        // 解析为 Instant
        // 解析为 OffsetDateTime
        OffsetDateTime offsetDateTime = OffsetDateTime.parse(utcTime);

        // 转换为 Instant
        Instant instant = offsetDateTime.toInstant();

        Date date = Date.from(instant);
        return date;
    }


    /**
     * 将日期格式化为   2023-09-30T08:12:34Z  类型
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/19 9:25
     */
    private String formatDate(Date date) {
        Instant instant = date.toInstant();
        return instant.toString();
    }


    /**
     * 通过gitlab api获取相关信息
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/18 20:31
     */
    private String getByApi(String apiUrl) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(apiUrl).openConnection();
        connection.setRequestMethod("GET");
        connection.setRequestProperty("PRIVATE-TOKEN", ACCESS_TOKEN);

        int responseCode = connection.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            reader.close();
            return response.toString();
        } else {
            throw new IOException("Failed to fetch repositories: HTTP " + responseCode);
        }
    }


    /**
     * 克隆工程
     * 克隆指定分支的代码
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/30 15:24
     */
    public void cloneProject(String url, String branchName, String workDir) {
        String cmd = String.format("git clone -b %s %s", branchName, url);
        String s = ShellUtil.executeBat(workDir, cmd);
    }


    /**
     * 拉取最新的code
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/31 10:42
     */
    public void pullCode(String workDir){
        String res = ShellUtil.executeBat(workDir, "git pull");
    }


    /**
     * 克隆默认分支的工程
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/30 16:05
     */
    public void cloneProjectWithDefaultBranch(String url, String workDir) {
        String cmd = String.format("git clone %s", url);
        ShellUtil.executeShell(workDir, cmd);
    }

    /**
     * 分析用户代码行数
     *
     * @param
     * @return
     * @author alei
     * @date 2025/3/28 18:46
     */
    public String analyseUserCodeCount(String gitDir, String beginDate, String endDate) throws IOException {
        if (StringUtils.isBlank(beginDate)) {
            beginDate = "1970-01-01";
        }
        if (StringUtils.isBlank(endDate)) {
            endDate = "2099-01-01";
        }


        String shellInfo = "#!/bin/sh\n" +
                "printf \"**** \" >> /d/res.txt\n" +
                "pwd >> /d/res.txt\n" +
                "printf \"\\n\"\n" +
                "\n" +
                "git log --all --since=beginDate --until=endDate  --format='%aN' | sort -u -r | while read name; do printf \"%25s\" \"$name\"; \\\n" +
                "git log --all --since=beginDate --until=endDate  --author=\"$name\" --pretty=tformat: --numstat | \\\n" +
                "awk '{ add += $1; subs += $2; loc += $1 - $2 } END { printf \"%15s %15s %15s \\n\", loc, add, subs }' \\\n" +
                "-; done   >> /d/res.txt\n" +
                "\n" +
                "printf \"\\n\\n\" >> /d/res.txt\n" +
                "exit;";

        shellInfo = shellInfo.replaceAll("beginDate", beginDate);
        shellInfo = shellInfo.replaceAll("endDate", endDate);

        File shellFile = new File(gitDir, "analyse-user-code-count.sh");
        shellFile.setWritable(true);
        BufferedWriter writer = new BufferedWriter(new FileWriter(shellFile));
        writer.write(shellInfo);
        shellFile.setExecutable(true);
        writer.close();

        String res = ShellUtil.executeShell(gitDir, "./analyse-user-code-count.sh");

        shellFile.delete();
        return res;
    }

}
